<?php
/**
 * Nuwani PHP IRC Bot Framework
 * Copyright (c) 2006-2010 The Nuwani Project
 *
 * Nuwani is a framework for IRC Bots built using PHP. Nuwani speeds up bot 
 * development by handling basic tasks as connection- and bot management, timers
 * and module managing. Features for your bot can easily be added by creating
 * your own modules, which will receive callbacks from the framework.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @copyright Copyright (c) 2006-2011 The Nuwani Project
 * @author Peter Beverloo <peter@lvp-media.com>
 * @author Dik Grapendaal <dik.grapendaal@gmail.com>
 * @see http://nuwani.googlecode.com
 */

//namespace Nuwani;

/**
 * Uno Nuwani Module
 * 
 * @copyright Copyright (c) 2011 The Nuwani Project, http://nuwani.googlecode.com/
 * @author Dik Grapendaal <dik.grapendaal@gmail.com>
 * @version $Id: IrcOutput.php 141 2011-07-23 21:47:01Z dik.grapendaal $
 * @package Modules
 */

class IrcOutput extends UnoOutput
{
        /**
         * The color map for the display of the colors of the cards. These will
         * be used to format the cards, so that they are a tad easier to read.
         * 
         * @var array
         */
        
        private static $sColorMap = array
        (
                UnoEngine :: COLOR_WILDCARD     => 'W',
                UnoEngine :: COLOR_BLUE         => '12B',
                UnoEngine :: COLOR_GREEN        => '03G',
                UnoEngine :: COLOR_RED          => '04R',
                UnoEngine :: COLOR_YELLOW       => '07Y'
        );
        
        /**
         * The number map for the numbers of the cards.
         * 
         * @var array
         */
        
        private static $sNumberMap = array
        (
                UnoEngine :: CARD_NONE          => '',
                UnoEngine :: CARD_0             => '0',
                UnoEngine :: CARD_1             => '1',
                UnoEngine :: CARD_2             => '2',
                UnoEngine :: CARD_3             => '3',
                UnoEngine :: CARD_4             => '4',
                UnoEngine :: CARD_5             => '5',
                UnoEngine :: CARD_6             => '6',
                UnoEngine :: CARD_7             => '7',
                UnoEngine :: CARD_8             => '8',
                UnoEngine :: CARD_9             => '9',
                UnoEngine :: CARD_DRAW_2        => 'D2',
                UnoEngine :: CARD_DRAW_4        => 'D4',
                UnoEngine :: CARD_REVERSE       => 'R',
                UnoEngine :: CARD_SKIP          => 'S'
        );
        
        /**
         * Reference to a bot object with which we can send messages to the
         * correct IRC channel.
         * 
         * @var Bot
         */
        
        private $mBot;
        
        /**
         * The channel the game is currently active in.
         * 
         * @var string
         */
        
        private $mChannel;
        
        /**
         * Buffer with lines that still need to be sent.
         * 
         * @var array
         */
        
        private $mBuffer = array ();
        
        /**
         * 
         */
        
        private $mPrivateBuffer = array ();
        
        /**
         * Outputs a line to IRC, using the Bot object and the channel as given
         * to setOutputInfo(). If there are no Bot object and channel available,
         * the line gets queued.
         * 
         * @param string $line The line to output.
         */
        
        public function outputPublic ($line)
        {
                if ($this -> mBot == null || $this -> mChannel == null)
                {
                        $this -> mBuffer [] = $line;
                        return;
                }
                
                $this -> rawOutputPublic ($line);
        }
        
        /**
         * Directly sends the given line to IRC. No checks done.
         * 
         * @param string $line The line to send.
         */
        
        private function rawOutputPublic ($line)
        {
                Uno :: ircPrivmsg ($this -> mBot, $this -> mChannel, $line);
        }
        
        /**
         * Outputs a line to the specified player, using the Bot object as given
         * to setOutputInfo(). If there is no Bot object available, the line
         * gets queued.
         * 
         * @param string $player The player to send it to.
         * @param string $line The line to output.
         */
        
        public function outputPrivate ($player, $line)
        {
                if ($this -> mBot == null)
                {
                        $this -> mPrivateBuffer [$player] = $line;
                        return;
                }
                
                $this -> rawOutputPrivate ($player, $line);
        }
        
        /**
         * Directly sends the given line to a person on IRC as a notice. No
         * checks done.
         * 
         * @param string $player The player to send it to.
         * @param string $line The line to send.
         */
        
        private function rawOutputPrivate ($player, $line)
        {
                Uno :: ircNotice ($this -> mBot, $player, $line);
        }
        
        /**
         * Processes the buffer, if there is any. It will send the contents of
         * the buffer to IRC. Recommended to call this method after output
         * information has been set.
         */
        
        private function processBuffer ()
        {
                // Still check this beforehand...
                if ($this -> mBot == null || $this -> mChannel == null)
                {
                        return;
                }
                
                // TODO: Rate limiting
                foreach ($this -> mPrivateBuffer as $player => $line)
                {
                        $this -> rawOutputPrivate ($player, $line);
                }
                
                foreach ($this -> mBuffer as $line)
                {
                        $this -> rawOutputPublic ($line);
                }
        }
        
        /**
         * Sets the output location information.
         * 
         * @param Bot $bot The Bot object to use.
         * @param string $channel The channel to send to.
         */
        
        public function setOutputInfo ($bot, $channel)
        {
                $this -> mBot = $bot;
                $this -> mChannel = $channel;
                
                // Now that we possess over output location specifics, dump the
                // buffer we might have.
                $this -> processBuffer ();
        }
        
        /**
         * Clears the output location information.
         */
        
        public function resetOutputInfo ()
        {
                $this -> mBot = null;
                $this -> mChannel = null;
        }
        
        /**
         * Formats a card as with the given properties.
         * 
         * @param int $color The color.
         * @param int $number The number.
         * @return string
         */
        
        public function formatCardProperties ($color, $number = UnoEngine :: CARD_NONE)
        {
                return self :: $sColorMap [$color] . self :: $sNumberMap [$number]
                     . ModuleBase :: CLEAR;
        }
        
        /**
         * Saves the scores with this output method.
         * 
         * @param int $score The score of the winning player.
         * @param string $winner The name of the winning player.
         * @param array $losers Array of with names of the losing players.
         */
        
        public function saveScores ($score, $winner, $losers)
        {
                $globalScores = Uno :: loadData ('Scores.dat');
                
                $channel = strtolower ($this -> mChannel);
                if (!isset ($globalScores [$channel]))
                {
                        $globalScores [$channel] = array ();
                }
                
                $players = array ();
                
                foreach ($losers as $nickname)
                {
                        $players [$nickname] = array
                        (
                                'Score'         => 0,
                                'Wins'          => 0,
                                'Losses'        => 1
                        );
                }
                
                $players [$winner] = array
                (
                        'Score'         => $score,
                        'Wins'          => 1,
                        'Losses'        => 0
                );
                
                foreach ($players as $player => $data)
                {
                        if (!isset ($globalScores [$channel] [$player]))
                        {
                                $globalScores [$channel] [$player] = $data;
                        }
                        else
                        {
                                $globalScores [$channel] [$player] ['Score']  += $data ['Score'];
                                $globalScores [$channel] [$player] ['Wins']   += $data ['Wins'];
                                $globalScores [$channel] [$player] ['Losses'] += $data ['Losses'];
                        }
                }
                
                Uno :: saveData ('Scores.dat', $globalScores);
        }
        
        /**
         * Save some arbitrary statistics about this Uno engine.
         * 
         * @param int $playTime The time the round took in seconds.
         * @param int $endScore The score achieved by the winner.
         */
        
        public function saveStatistics ($playTime, $endScore)
        {
                $globalStats = Uno :: loadData ('Stats.dat');
                
                $channel = strtolower ($this -> mChannel);
                if (!isset ($globalStats [$channel]))
                {
                        $globalStats [$channel] = array ();
                }
                
                if (!isset ($globalStats [$channel] ['Time']))
                {
                        $globalStats [$channel] ['Time'] = array
                        (
                                'Shortest'      => $playTime,
                                'Longest'       => $playTime,
                                'Total'         => $playTime
                        );
                }
                else
                {
                        $shortest = & $globalStats [$channel] ['Time'] ['Shortest'];
                        $longest  = & $globalStats [$channel] ['Time'] ['Longest'];
                        
                        $shortest = $shortest > $playTime ? $playTime : $shortest;
                        $longest  = $longest < $playTime ? $playTime : $longest;
                        
                        $globalStats [$channel] ['Time'] ['Total'] += $playTime;
                }
                
                if (!isset ($globalStats [$channel] ['Score']))
                {
                        $globalStats [$channel] ['Score'] = array
                        (
                                'Lowest'        => $endScore,
                                'Highest'       => $endScore,
                                'Total'         => $endScore
                        );
                }
                else
                {
                        $highest = & $globalStats [$channel] ['Score'] ['Highest'];
                        $lowest  = & $globalStats [$channel] ['Score'] ['Lowest'];
                        
                        $highest = $highest < $endScore ? $endScore : $highest;
                        $lowest  = $lowest > $endScore ? $endScore : $lowest;
                        
                        $globalStats [$channel] ['Score'] ['Total'] += $endScore;
                }
                
                Uno :: saveData ('Stats.dat', $globalStats);
        }
}